home *** CD-ROM | disk | FTP | other *** search
- Listing 6.
-
- /* errmgr.cpp - error manager */
- /* has been trimmed to fit; see the monthly code */
- /* disk for the complete source. */
-
- #include <stdio.h>
- #include <ctype.h>
- #include <stdlib.h>
- #include <stdarg.h>
- #include <string.h>
- #include "complain.hpp"
- #include "utils.hpp"
- #include "c_failur.hpp"
- #include "errmgr.hpp"
-
- /* an element in the list of complaint dictionaries: */
-
- class error_dict_list {
- public:
- error_dict_list(error_dict_list *curr,
- complaint_dict *new_dict);
- error_dict_list *prev;
- complaint_dict *dict;
- };
-
- error_mgr err_mgr;
-
- /* is_set_up is set when err_mgr is initialized. no */
- /* other error manager can be active. */
-
- int error_mgr::is_set_up = 0;
-
- /* asserts_off is 0 when assertions are to be checked. */
-
- #ifndef NDEBUG
- int error_mgr::asserts_off = 0;
- #else
- int error_mgr::asserts_off = 1;
- #endif
-
- failure_handler *error_mgr::curr_handler = 0,
- *error_mgr::default_handler = 0;
- error_dict_list *error_mgr::error_dicts = 0;
- ptr_stack *error_mgr::handler_stack = 0;
-
- static char line[512]; /* should be managed buffer */
-
- static int proper_format(const char *pgm_text,
- const char *user_text);
- static char format_specifier(const char **fmt);
-
- error_mgr::error_mgr(void) /* constructor */
- {
- setup();
- }
-
- error_mgr::~error_mgr(void) /* destructor */
- {
- delete default_handler;
- delete handler_stack;
- }
-
- void error_mgr::setup(void)
- {
- /* create default handler and empty dictionary */
- /* list, allocate stack for failure_handlers, */
- /* make sure no other error_mgr is present. */
- if (this != &err_mgr) /* just in case... */
- err_mgr.fail("Duplicate error_mgr defined.\n");
- if (is_set_up)
- return;
- is_set_up = 1;
- curr_handler = default_handler =
- new failure_handler;
- handler_stack = new ptr_stack;
- }
-
- int error_mgr::define_dictionary(const char *filename)
- {
- /* add a new dictionary to the list. it's */
- /* ignored if any errors are found. */
- counting_failure_handler *our_handler;
- complaint_dict *new_dict;
-
- setup();
- /* we count errors in the file with our_handler. */
- our_handler = new counting_failure_handler;
- new_dict = new complaint_dict(filename);
- if (our_handler->errors_logged() ||
- our_handler->warns_logged()) {
- delete new_dict;
- warn("$bad_error_dict: dictionary file \"%s\" "
- "has errors - will not be used\n",
- filename);
- delete our_handler;
- return 0;
- }
- error_dicts = new error_dict_list(
- error_dicts,new_dict);
- delete our_handler;
- return 1;
- }
-
- failure_handler *error_mgr::define_handler(
- failure_handler *new_handler)
- {
- /* install a new error handler, pushing the */
- /* previous handler onto a stack. */
- failure_handler *old_handler;
-
- setup();
- handler_stack->push(curr_handler);
- old_handler = curr_handler;
- if (new_handler == 0) /* bomb-proof */
- curr_handler = default_handler;
- else
- curr_handler = new_handler;
- return old_handler;
- }
-
- failure_handler *error_mgr::restore_handler(void)
- {
- /* return to the previous error handler. */
- failure_handler *old_handler;
-
- setup();
- old_handler = curr_handler;
- curr_handler = (failure_handler *)
- (handler_stack->pop());
- if (curr_handler == 0)
- curr_handler = default_handler;
- return old_handler;
- }
-
- const char *error_mgr::message(
- const char *fmt,char *msg_line,
- int linelen)
- {
- /* search for the replacement text, writing */
- /* it into msg_line if found. return a pointer */
- /* to the start of the message, ready to print. */
- const char *new_fmt,*key,*keystart;
- char *key_alloc,*buf;
- int keylen;
-
- /* this routine calls setup() for fail(), */
- /* vfail(), etc. */
- setup();
- ASSERT(fmt != msg_line);
- msg_line[0] = '\0';
- if (*fmt != '$') { /* not keyed? */
- strncpy(msg_line,fmt,linelen);
- msg_line[linelen - 1] = '\0';
- return msg_line;
- }
-
- /* extract the key. */
- key = ++fmt; /* skip '$' */
- key += skipblanks(key);
- keylen = skip_ident(key); /* fetch key name */
- new_fmt = key + keylen; /* skip key name */
- new_fmt += skipblanks(key); /* skip to ':' */
-
- /* error text here is not replaceable - */
- /* recursive calls would be dangerous. */
- if (keylen == 0 || *new_fmt != ':') {
- sprintf(msg_line,"Keyed error message format "
- "string is malformed\n%s",fmt);
- }
- else {
- key_alloc = newstring(key,keylen);
- ++new_fmt; /* skip ':' */
- new_fmt += skipblanks(new_fmt);
- if (find_replacement(key_alloc,msg_line,
- linelen)) {
- if (!proper_format(new_fmt,msg_line)) {
- /* invalid; dictionary text left at */
- /* front of resulting error message. */
- buf = msg_line + strlen(msg_line);
- sprintf(buf,"Replacement text for "
- "keyed error message is "
- "malformed\n%s",fmt);
- }
- }
- else { /* use program version */
- strncpy(msg_line,new_fmt,linelen);
- msg_line[linelen - 1] = '\0';
- }
- free(key_alloc);
- }
- return msg_line;
- }
-
- int error_mgr::find_replacement(
- const char *key,char *msg_line,
- int linelen)
- {
- /* search for message replacement. returns 1 */
- /* if found, 0 if not. */
- error_dict_list *curr_dict;
-
- for (curr_dict = error_dicts; curr_dict != 0;
- curr_dict = curr_dict->prev)
- if (curr_dict->dict->key_defined(key))
- break;
- return curr_dict != 0 &&
- curr_dict->dict->complaint_text(
- key,msg_line,linelen);
- }
-
- static int proper_format(const char *pgm_text,
- const char *user_text)
- {
- /* return 1 if the printf() format specifiers */
- /* in user_text match the ones in pgm_text. */
- char pgm_fmt,user_fmt;
-
- do {
- pgm_fmt = format_specifier(&pgm_text);
- user_fmt = format_specifier(&user_text);
- if (pgm_fmt != user_fmt)
- return 0;
- } while (*pgm_text || *user_text);
- return 1;
- }
-
- static char format_specifier(const char **fmt)
- {
- /* skip to the next '%' specifier (if any) */
- /* and return a unique letter indicating its */
- /* type. advances *fmt past the specifier. */
- const char *s = *fmt; /* *s vs. **fmt */
- char c;
-
- for (;;) { /* skip to '%' */
- while (*s && *s != '%')
- ++s;
- if (*s == '\0')
- break;
- ++s; /* skip '%' */
- if (*s == '%') /* "%%" prints '%' */
- ++s; /* not specifier */
- else
- break;
- }
- if (*s == '\0') {
- *fmt = s; /* update caller var */
- return '\0';
- }
-
- /* skip to the specifier letter. */
- while (*s && !isspace(*s) && !isalpha(*s))
- ++s;
- if (!isalpha(*s)) {
- *fmt = s; /* update caller var */
- return '\0'; /* bad specifier */
- }
-
- /* any 'l' is assumed to be long. */
- if (*s == 'l' && isalpha(*(s + 1))) {
- *fmt = s + 2; /* skip l, specifier */
- return 'l';
- }
-
- /* map the specifier into a canonical value. */
- c = tolower(*s);
- *fmt = s + 1; /* update caller var */
- switch(c) {
- case 'e': case 'f': case 'g':
- return 'e'; /* floating point */
- case 'o': case 'u': case 'x':
- case 'd': case 'i': case 'b':
- return 'd'; /* int */
- default: /* all others map */
- return c; /* to themselves */
- } /* end of switch(c) */
- /* NOTREACHED */
- }
-
- void error_mgr::fail(const char *fmt,...)
- {
- /* print the message and exit. */
- va_list ap;
-
- va_start(ap,fmt);
- vfail(fmt,ap); /* won't return */
- /* NOTREACHED */
- va_end(ap);
- }
-
- void error_mgr::vfail(const char *fmt,va_list ap)
- {
- /* fail() with a va_list already built. if */
- /* the handler doesn't exit we force it. */
- fmt = message(fmt,line,sizeof(line));
- curr_handler->fail(fmt,ap);
- exit(EXIT_FAILURE); /* just in case! */
- }
-
- void error_mgr::error(const char *fmt,...)
- {
- /* print the message and ask for permission to */
- /* continue. */
- va_list ap;
-
- va_start(ap,fmt);
- verror(fmt,ap);
- va_end(ap);
- }
-
- void error_mgr::verror(const char *fmt,va_list ap)
- {
- /* error() with a va_list already built. */
- /* we replace the message if possible. */
- fmt = message(fmt,line,sizeof(line));
- curr_handler->error(fmt,ap);
- }
-
- int error_mgr::set_assert_flag(int asserts_on)
- {
- /* enable or disable assertions at run time. */
- int retval = asserts_off;
-
- /* set to opposite state because we want to */
- /* short-circuit assertion evaluation. */
- asserts_off = !asserts_on;
- return !retval;
- }
-
- int error_mgr::assert_failed(
- const char *exp,const char *fname,
- unsigned linenum)
- {
- /* an assertion has failed. */
- error("Assertion failed - file %s, line %u:\n%s\n",
- fname,linenum,exp);
- return 1; /* needed for macro */
- }